﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace NetworkScanner
{
    class Host : IComparable<Host>
    {
        /// <summary>
        /// Direccion IP del host
        /// </summary>
        public IPAddress Addr { get; private set; }

        /// <summary>
        /// campo para la propiedad Hostname
        /// </summary>
        private string HostNameField = "resolviendo...";

        /// <summary>
        /// Propiedad Hostname, al setear se dispara el evento HostNameUpdate
        /// </summary>
        public string Hostname
        {
            get
            {
                lock (this)
                {
                    return HostNameField;
                }
            }
            private set
            {
                lock (this)
                {
                    HostNameField = value;
                }
                if (HostNameUpdate != null)
                {
                    HostNameUpdate(this, new NetworkEventArgs(NetworkEventArgs.TipoEvento.HostNameResuelto, 0));
                }

            }
        }
        /// <summary>
        /// Direccion fisica (MAC) del host
        /// </summary>
        public PhysicalAddress MAC { get; private set; }

        /// <summary>
        /// Marca de la interfaz de red del host, se obtiene a partir de MAC
        /// </summary>
        public string Vendor { get; private set; }

        /// <summary>
        /// Latencia del ping
        /// </summary>
        public long Latencia { get; set; }
        
        /// <summary>
        /// Campo con informacion adicional del host
        /// </summary>
        private string DetailsField = "";

        /// <summary>
        /// Datos adicionales acerca del host
        /// </summary>
        public string Details { get { return DetailsField; } set { DetailsField = value; } }

        /// <summary>
        /// evento para notificar la resolucion del hostname
        /// </summary>
        public event NetworkEventHandler HostNameUpdate;

        /// <summary>
        /// Crea una instancia de la clase Host
        /// </summary>
        /// <param name="add">Direccion IP del nuevo host</param>
        /// <param name="resolverMAC">Indica si debe resolver la MAC del host</param>
        public Host(IPAddress add, bool resolverMAC)
        {
            Addr = add;
            try
            {
                Dns.BeginGetHostEntry(Addr, new AsyncCallback(GetHostEntryCallback), this);
            }
            catch (Exception) { }

            if (resolverMAC)
            {
                MAC = ARPTools.ARP(Addr);
                Vendor = OUILookup.MACtoVendorLookup(MAC);
            }
            else
            {
                MAC = PhysicalAddress.None;
                Vendor = "";
            }            
        }

        /// <summary>
        /// Crea una instancia de la clase Host
        /// </summary>
        /// <param name="add">Direccion IP del nuevo host</param>
        /// <param name="pi">MAC del nuevo host</param>
        public Host(IPAddress add, PhysicalAddress mac)
        {
            Addr = add;
            try
            {
                Dns.BeginGetHostEntry(Addr, new AsyncCallback(GetHostEntryCallback), this);
            }
            catch (Exception) { }

            MAC = mac;
            Vendor = OUILookup.MACtoVendorLookup(MAC);
        }

        /// <summary>
        /// Oyente que se invoca al completar la resolucion de hostname
        /// </summary>
        /// <param name="ar">Resultados de la resolucion de nombres</param>
        private void GetHostEntryCallback(IAsyncResult ar)
        {
            Host esteHost = (Host)ar.AsyncState;
            try
            {
                IPHostEntry IPs = Dns.EndGetHostEntry(ar);
                esteHost.Hostname = IPs.HostName;
            }
            catch (SocketException)
            {
                esteHost.Hostname = "";
            }
            catch (Exception)
            {
                esteHost.Hostname = "error";
            }
        }




        /// <summary>
        /// Implementacion de interfaz para comparar elementos segun la IP.
        /// y asi ordenarlos en una coleccion
        /// </summary>
        /// <param name="other">host con quien realizar comparacion</param>
        /// <returns>-1, 1 o 0 segun si la IP es menor, mayor o igual al parametro respectivamente</returns>
        public int CompareTo(Host other)
        {
            return this.Addr.CompareTo(other.Addr);
        }
    }
}
